package com.jvyou.app.ds.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.jvyou.app.common.domain.entity.SysFile;
import com.jvyou.app.common.enums.HttpCodeEnum;
import com.jvyou.app.common.exception.AppException;
import com.jvyou.app.common.utils.file.PathUtils;
import com.jvyou.app.ds.config.AppConfig;
import com.jvyou.app.ds.mapper.SysFileMapper;
import com.jvyou.app.ds.service.SysFileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;


/**
 * 文件表(SysFile)表服务实现类
 *
 * @author 橘柚
 * @since 2025-03-07 11:00:41
 */
@Service("sysFileService")
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {

    @Autowired
    private AppConfig appConfig;

    @Override
    public SysFile upload(MultipartFile multipartFile) {
        if (multipartFile == null || multipartFile.isEmpty()) {
            throw new AppException(HttpCodeEnum.FILE_EMPTY);
        }
        // 1. 将相对路径转化为绝对路径
        String destPath = new File(appConfig.getFile().getUploadPath()).getAbsolutePath();
        // 2. 上传时候的文件名
        String originalFilename = multipartFile.getOriginalFilename();
        // 3. 文件大小
        double size = multipartFile.getSize();
        // 4.文件类型
        if (originalFilename == null || !originalFilename.contains(".")) {
            throw new AppException(HttpCodeEnum.FILE_PARSE_FAIL);
        }
        String type = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
        //生成文件的存放路径
        String path = PathUtils.generateFilePath(originalFilename);
        //新的文件地址，绝对路径+新的文件名称
        File uploadFile = new File(destPath + "/" + path);

        //判断配置的文件目录是否存在，若不存在则创建一个新的文件目录
        File parentFile = uploadFile.getParentFile();
        if (!parentFile.exists()) {
            boolean mkdirs = parentFile.mkdirs();
            if (!mkdirs) {
                throw new AppException(HttpCodeEnum.SYSTEM_ERROR, "上传文件创建父级文件夹出错");
            }
        }
        try {
            //获取文件的md5,通过对比文件md5，防止上传相同内容的文件
            String md5 = DigestUtils.md5DigestAsHex(multipartFile.getInputStream());
            //通过MD5来查询文件
            SysFile dbFiles = this.getFileByMD5(md5);
            if (dbFiles != null) {//如果数据库存在相同文件，直接获取url
                return dbFiles;
            } else {//如果数据库不存在相同文件，先存储到本地磁盘，再设置文件url
                multipartFile.transferTo(uploadFile);//把获取到的文件存储带磁盘目录
            }

            //将文件存储到数据库
            SysFile saveFile = new SysFile();
            saveFile.setFilesName(originalFilename);
            saveFile.setType(type);
            saveFile.setSize(size / 1024);//（单位：KB）
            saveFile.setUrl("/" + path);
            saveFile.setMd5(md5);
            //保存操作
            save(saveFile);
            return saveFile;
        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new AppException(HttpCodeEnum.SYSTEM_ERROR, "文件上传失败");
        }
    }

    /**
     * 通过文件MD5查询文件
     *
     * @param md5 文件md5
     * @return 文件信息
     */
    private SysFile getFileByMD5(String md5) {
        //查找数据库是否已经存在一样的图片
        LambdaUpdateWrapper<SysFile> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(SysFile::getMd5, md5);
        List<SysFile> filesList = list(wrapper);
        return filesList.isEmpty() ? null : filesList.get(0);
    }

    @Transactional
    @Override
    public void download(Long uuid, HttpServletResponse response) {
        SysFile sysFile = getById(uuid);
        if (sysFile == null) {
            throw new AppException(HttpCodeEnum.SYSTEM_ERROR, "文件不存在");
        }
        downloadByPath(sysFile.getUrl(), response);
    }

    @Transactional
    @Override
    public void downloadByPath(String path, HttpServletResponse response) {
        File uploadFile = new File(appConfig.getFile().getUploadPath() + path);

        // 判断文件是否存在
        if (!uploadFile.exists()) {
            throw new AppException(HttpCodeEnum.SYSTEM_ERROR, "文件不存在");
        }

        String fileName = uploadFile.getName();
        String fileExtension = getFileExtension(fileName);

        // 获取 MIME 类型和 Content-Disposition
        String mimeType = getMimeType(fileExtension);
        boolean isInline = isInlineFile(fileExtension);

        try {
            // 读取文件字节流
            byte[] buffer = Files.readAllBytes(uploadFile.toPath());
            // 设置响应头
            response.setContentType(mimeType);
            response.setHeader("Content-Disposition",
                    (isInline ? "inline" : "attachment") + "; filename=" + URLEncoder.encode(fileName, "UTF-8"));

            // 输出文件流
            try (ServletOutputStream os = response.getOutputStream()) {
                os.write(buffer);
                os.flush();
            }
        } catch (IOException e) {
            log.error("文件下载失败", e);
            throw new AppException(HttpCodeEnum.SYSTEM_ERROR, "文件下载失败，原因：" + e.getMessage());
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String fileName) {
        int lastIndex = fileName.lastIndexOf(".");
        return (lastIndex == -1) ? "" : fileName.substring(lastIndex + 1).toLowerCase();
    }

    /**
     * 根据文件扩展名获取 MIME 类型
     */
    private String getMimeType(String extension) {
        Map<String, String> mimeTypes = ImmutableMap.of(
                "jpg", "image/jpeg",
                "jpeg", "image/jpeg",
                "png", "image/png",
                "gif", "image/gif",
                "pdf", "application/pdf"
        );
        return mimeTypes.getOrDefault(extension, "application/octet-stream");
    }

    /**
     * 判断是否是 inline 文件（可在浏览器打开）
     */
    private boolean isInlineFile(String extension) {
        return extension.equals("jpg") || extension.equals("jpeg") ||
                extension.equals("png") || extension.equals("gif") ||
                extension.equals("pdf");
    }


}
